<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>遍历文档</title>
</head>

<body>
    <div class="wrapper">
        <ul>
            <li>111</li>
            <li>112</li>
            <li> <input type="text" name="username" placeholder="用户名"> </li>
        </ul>
        <ul>
            <li>121</li>
            <li>122</li>
            <li> <input type="radio" name="gender" value="male"> </li>
        </ul>
    </div>

    <div class="wrapper">
        <ul>
            <li>211</li>
            <li>212</li>
        </ul>
        <ul>
            <li>221</li>
            <li>222</li>
        </ul>
    </div>

    <div class="wrapper">
        <ul>
            <li>311</li>
            <li>312</li>
        </ul>
        <ul>
            <li>321</li>
            <li>322</li>
        </ul>
    </div>

    <script type="text/javascript">
        (() => {
            let type = function(nodeType) {
                switch (nodeType) {
                    case Node.DOCUMENT_NODE:
                        return 'DOCUMENT';
                    case Node.DOCUMENT_TYPE_NODE:
                        return 'DOCUMENT_TYPE';
                    case Node.ELEMENT_NODE:
                        return 'ELEMENT';
                    case Node.ATTRIBUTE_NODE:
                        return 'ATTRIBUTE';
                    case Node.COMMENT_NODE:
                        return 'COMMENT';
                    case Node.TEXT_NODE:
                        return 'TEXT';
                    default:
                        return nodeType;
                }
            }

            let traversal = function(node) {
                    // 非严格模式下，在函数内可以通过 arguments.callee 来获得当前函数的引用
                    let traversalFunction = arguments.callee;

                    /* = = = 处理文本节点( Node.TEXT_NODE ) = = = */
                    let text = '';
                    if (node.nodeType === Node.TEXT_NODE) {
                        let tc = node.textContent;
                        // 文本内容 既不以换行符为开始也不以换行符为结尾
                        if (!tc.startsWith('\n') && !tc.endsWith('\n')) {
                            text = tc;
                        }
                    }

                    /* = = = 输出当前节点的相关信息 = = = */
                    console.log(node.nodeName, type(node.nodeType), text);

                    /* = = = 处理当前元素的属性 = = = */
                    // 判断当前节点是否是个元素(element)
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        // 获取元素的所有属性名组成的数组
                        let attrNames = node.getAttributeNames();
                        attrNames.forEach(function(name) {
                            // 根据属性名称获取指定属性的值
                            let value = node.getAttribute(name);
                            console.log('\t', name, value);
                        });
                    }

                    /* = = = 处理当前元素的子元素 = = = */
                    // 判断参数所传入的节点是否拥有子节点
                    if (node.hasChildNodes()) {
                        let childs = node.childNodes; // 返回所有子节点组成的集合(NodeList)
                        let childArray = Array.from(childs); // 将 类数组实例 转换为 数组实例
                        childArray.forEach(function(e) {
                            // 按照遍历函数中的处理方式继续处理子节点
                            traversalFunction(e); // 调用遍历函数继续处理 e 对应节点 
                        });
                    }
                }
                // Document 接口 继承了 Node 接口
                // 所以 document 可以当作 node 来对待
            let node = document; // 得到整个文档对象

            traversal(node);
        })();
    </script>
</body>

</html>